package main

import (
	"database/sql"
	"encoding/json"
	"flag"
	"fmt"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"text/template"
	"time"

	_ "github.com/go-sql-driver/mysql"
)

// 字段信息
type Field struct {
	Name       string
	Type       string
	Tag        string
	JsonName   string
	ColumnName string
	Comment    string
	NotNull    bool
}

// 模板数据
type TemplateData struct {
	PackageName  string
	StructName   string
	TableName    string
	TableComment string
	Fields       []Field
	UniqueFields []Field
	ModuleName   string
	Author       string
	Date         string
	RoutePrefix  string
	VarName      string
}

func main() {
	// 解析命令行参数
	configFile := flag.String("config", "config.json", "配置文件路径")
	tableName := flag.String("table", "", "指定要生成的表名")
	flag.Parse()

	// 加载配置
	config, err := loadConfig(*configFile)
	if err != nil {
		fmt.Printf("加载配置失败: %v\n", err)
		os.Exit(1)
	}

	// 连接数据库
	dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local",
		config.DB.Username, config.DB.Password, config.DB.Host, config.DB.Port, config.DB.Database)
	db, err := sql.Open("mysql", dsn)
	if err != nil {
		fmt.Printf("连接数据库失败: %v\n", err)
		os.Exit(1)
	}
	defer db.Close()

	// 确定要处理的表
	tables := config.Generate.Tables
	if *tableName != "" {
		tables = []string{*tableName}
	}

	// 处理每个表
	for _, table := range tables {
		// 生成模型
		if err := generateModel(db, table, config); err != nil {
			fmt.Printf("生成模型失败 [%s]: %v\n", table, err)
		} else {
			fmt.Printf("成功生成模型 [%s]\n", table)
		}

		// 生成服务层
		if err := generateService(db, table, config); err != nil {
			fmt.Printf("生成服务失败 [%s]: %v\n", table, err)
		} else {
			fmt.Printf("成功生成服务 [%s]\n", table)
		}

		// 生成API层
		if err := generateAPI(db, table, config); err != nil {
			fmt.Printf("生成API失败 [%s]: %v\n", table, err)
		} else {
			fmt.Printf("成功生成API [%s]\n", table)
		}

		// 生成路由
		if err := generateRouter(db, table, config); err != nil {
			fmt.Printf("生成路由失败 [%s]: %v\n", table, err)
		} else {
			fmt.Printf("成功生成路由 [%s]\n", table)
		}

		// 注册路由
		structName := toCamelCase(table, true)
		if err := registerRouter(structName, config); err != nil {
			fmt.Printf("注册路由失败 [%s]: %v\n", table, err)
		} else {
			fmt.Printf("成功注册路由 [%s]\n", table)
		}

		// 更新API入口文件
		if err := updateAPIEnter(structName, config); err != nil {
			fmt.Printf("更新API入口文件失败 [%s]: %v\n", table, err)
		} else {
			fmt.Printf("成功更新API入口文件 [%s]\n", table)
		}
	}
}

// 加载配置文件
func loadConfig(path string) (*Config, error) {
	data, err := os.ReadFile(path)
	if err != nil {
		return nil, err
	}

	var config Config
	if err := json.Unmarshal(data, &config); err != nil {
		return nil, err
	}

	// 设置默认值
	if config.Generate.ModuleName == "" {
		config.Generate.ModuleName = "business"
	}

	return &config, nil
}

// 生成模型代码
func generateModel(db *sql.DB, tableName string, config *Config) error {
	// 获取表信息
	var tableComment string
	err := db.QueryRow("SELECT table_comment FROM information_schema.tables WHERE table_schema = ? AND table_name = ?",
		config.DB.Database, tableName).Scan(&tableComment)
	if err != nil {
		return fmt.Errorf("获取表注释失败: %v", err)
	}

	// 获取字段信息
	rows, err := db.Query(`
		SELECT column_name, column_type, is_nullable, column_comment
		FROM information_schema.columns
		WHERE table_schema = ? AND table_name = ?
		ORDER BY ordinal_position
	`, config.DB.Database, tableName)
	if err != nil {
		return fmt.Errorf("获取字段信息失败: %v", err)
	}
	defer rows.Close()

	var fields []Field
	for rows.Next() {
		var columnName, columnType, isNullable, columnComment string
		if err := rows.Scan(&columnName, &columnType, &isNullable, &columnComment); err != nil {
			return fmt.Errorf("读取字段信息失败: %v", err)
		}

		// 跳过主键字段(假设是id)，因为已经在Models中包含
		if strings.ToLower(columnName) == "id" || strings.ToLower(columnName) == "created_at" || strings.ToLower(columnName) == "updated_at" || strings.ToLower(columnName) == "deleted_at" {
			continue
		}

		// 转换为Go类型
		goType := convertToGoType(columnType)

		// 构造gorm标签
		tag := fmt.Sprintf("column:%s", columnName)
		if columnComment != "" {
			tag += fmt.Sprintf(";comment:%s", columnComment)
		}

		notNull := isNullable == "NO"
		if notNull {
			tag += ";not null"
		}

		// 构造json名称
		jsonName := toCamelCase(columnName, false)

		fields = append(fields, Field{
			Name:       toCamelCase(columnName, true),
			Type:       goType,
			Tag:        tag,
			JsonName:   jsonName,
			ColumnName: columnName,
			Comment:    columnComment,
			NotNull:    notNull,
		})
	}

	// 准备模板数据
	structName := toCamelCase(tableName, true)
	packageName := strings.ToLower(structName)
	moduleName := config.Generate.ModuleName

	data := TemplateData{
		PackageName:  packageName,
		StructName:   structName,
		TableName:    tableName,
		TableComment: tableComment,
		Fields:       fields,
		ModuleName:   moduleName,
		Author:       config.Generate.Author,
		Date:         time.Now().Format("2006-01-02"),
	}

	// 加载模板
	tmpl, err := template.ParseFiles("template/model.tmpl")
	if err != nil {
		return fmt.Errorf("加载模板失败: %v", err)
	}

	// 创建输出目录
	outputDir := filepath.Join(config.Generate.OutputPath, "internal/model", moduleName, packageName)
	if err := os.MkdirAll(outputDir, 0755); err != nil {
		return fmt.Errorf("创建输出目录失败: %v", err)
	}

	// 创建输出文件
	outputFile := filepath.Join(outputDir, "model.go")
	file, err := os.Create(outputFile)
	if err != nil {
		return fmt.Errorf("创建输出文件失败: %v", err)
	}
	defer file.Close()

	// 执行模板
	if err := tmpl.Execute(file, data); err != nil {
		return fmt.Errorf("执行模板失败: %v", err)
	}

	fmt.Printf("模型代码已生成: %s\n", outputFile)
	return nil
}

// 生成服务层代码
func generateService(db *sql.DB, tableName string, config *Config) error {
	// 获取表信息
	var tableComment string
	err := db.QueryRow("SELECT table_comment FROM information_schema.tables WHERE table_schema = ? AND table_name = ?",
		config.DB.Database, tableName).Scan(&tableComment)
	if err != nil {
		return fmt.Errorf("获取表注释失败: %v", err)
	}

	// 获取字段信息
	rows, err := db.Query(`
		SELECT column_name, column_type, is_nullable, column_comment
		FROM information_schema.columns
		WHERE table_schema = ? AND table_name = ?
		ORDER BY ordinal_position
	`, config.DB.Database, tableName)
	if err != nil {
		return fmt.Errorf("获取字段信息失败: %v", err)
	}
	defer rows.Close()

	var fields []Field
	var uniqueFields []Field
	for rows.Next() {
		var columnName, columnType, isNullable, columnComment string
		if err := rows.Scan(&columnName, &columnType, &isNullable, &columnComment); err != nil {
			return fmt.Errorf("读取字段信息失败: %v", err)
		}

		// 跳过主键字段(假设是id)，因为已经在Models中包含
		if strings.ToLower(columnName) == "id" {
			continue
		}

		// 转换为Go类型
		goType := convertToGoType(columnType)

		// 构造gorm标签
		tag := fmt.Sprintf("column:%s", columnName)
		if columnComment != "" {
			tag += fmt.Sprintf(";comment:%s", columnComment)
		}

		notNull := isNullable == "NO"
		if notNull {
			tag += ";not null"
		}

		// 构造json名称
		jsonName := toCamelCase(columnName, false)

		field := Field{
			Name:       toCamelCase(columnName, true),
			Type:       goType,
			Tag:        tag,
			JsonName:   jsonName,
			ColumnName: columnName,
			Comment:    columnComment,
			NotNull:    notNull,
		}

		fields = append(fields, field)

		// 假设唯一字段（这里可以根据实际情况调整）
		if strings.Contains(columnName, "code") || strings.Contains(columnName, "number") ||
			strings.Contains(columnName, "name") || strings.Contains(columnName, "key") {
			uniqueFields = append(uniqueFields, field)
		}
	}

	// 准备模板数据
	structName := toCamelCase(tableName, true)
	packageName := strings.ToLower(structName)
	moduleName := config.Generate.ModuleName

	data := TemplateData{
		PackageName:  packageName,
		StructName:   structName,
		TableName:    tableName,
		TableComment: tableComment,
		Fields:       fields,
		UniqueFields: uniqueFields,
		ModuleName:   moduleName,
		Author:       config.Generate.Author,
		Date:         time.Now().Format("2006-01-02"),
	}

	// 加载模板
	tmpl, err := template.ParseFiles("template/service.tmpl")
	if err != nil {
		return fmt.Errorf("加载服务模板失败: %v", err)
	}

	// 创建输出目录
	outputDir := filepath.Join(config.Generate.OutputPath, "internal/service", moduleName)
	if err := os.MkdirAll(outputDir, 0755); err != nil {
		return fmt.Errorf("创建输出目录失败: %v", err)
	}

	// 创建输出文件
	outputFile := filepath.Join(outputDir, strings.ToLower(structName)+"Service.go")
	file, err := os.Create(outputFile)
	if err != nil {
		return fmt.Errorf("创建输出文件失败: %v", err)
	}
	defer file.Close()

	// 执行模板
	if err := tmpl.Execute(file, data); err != nil {
		return fmt.Errorf("执行模板失败: %v", err)
	}

	fmt.Printf("服务层代码已生成: %s\n", outputFile)
	return nil
}

// 生成API层代码
func generateAPI(db *sql.DB, tableName string, config *Config) error {
	// 获取表信息
	var tableComment string
	err := db.QueryRow("SELECT table_comment FROM information_schema.tables WHERE table_schema = ? AND table_name = ?",
		config.DB.Database, tableName).Scan(&tableComment)
	if err != nil {
		return fmt.Errorf("获取表注释失败: %v", err)
	}

	// 准备模板数据
	structName := toCamelCase(tableName, true)
	packageName := strings.ToLower(structName)
	moduleName := config.Generate.ModuleName
	routePrefix := strings.ToLower(tableName)
	varName := strings.ToLower(structName[:1]) + structName[1:]

	data := TemplateData{
		PackageName:  packageName,
		StructName:   structName,
		TableName:    tableName,
		TableComment: tableComment,
		ModuleName:   moduleName,
		Author:       config.Generate.Author,
		Date:         time.Now().Format("2006-01-02"),
		RoutePrefix:  routePrefix,
		VarName:      varName,
	}

	// 加载模板
	tmpl, err := template.ParseFiles("template/api.tmpl")
	if err != nil {
		return fmt.Errorf("加载API模板失败: %v", err)
	}

	// 创建输出目录
	outputDir := filepath.Join(config.Generate.OutputPath, "internal/api/v1", moduleName)
	if err := os.MkdirAll(outputDir, 0755); err != nil {
		return fmt.Errorf("创建输出目录失败: %v", err)
	}

	// 创建输出文件
	outputFile := filepath.Join(outputDir, strings.ToLower(structName)+"Handler.go")
	file, err := os.Create(outputFile)
	if err != nil {
		return fmt.Errorf("创建输出文件失败: %v", err)
	}
	defer file.Close()

	// 执行模板
	if err := tmpl.Execute(file, data); err != nil {
		return fmt.Errorf("执行模板失败: %v", err)
	}

	fmt.Printf("API层代码已生成: %s\n", outputFile)
	return nil
}

// 生成路由代码
func generateRouter(db *sql.DB, tableName string, config *Config) error {
	// 获取表信息
	var tableComment string
	err := db.QueryRow("SELECT table_comment FROM information_schema.tables WHERE table_schema = ? AND table_name = ?",
		config.DB.Database, tableName).Scan(&tableComment)
	if err != nil {
		return fmt.Errorf("获取表注释失败: %v", err)
	}

	// 准备模板数据
	structName := toCamelCase(tableName, true)
	packageName := strings.ToLower(structName)
	moduleName := config.Generate.ModuleName
	routePrefix := strings.ToLower(tableName)
	varName := strings.ToLower(structName[:1]) + structName[1:]

	data := TemplateData{
		PackageName:  packageName,
		StructName:   structName,
		TableName:    tableName,
		TableComment: tableComment,
		ModuleName:   moduleName,
		Author:       config.Generate.Author,
		Date:         time.Now().Format("2006-01-02"),
		RoutePrefix:  routePrefix,
		VarName:      varName,
	}

	// 加载模板
	tmpl, err := template.ParseFiles("template/router.tmpl")
	if err != nil {
		return fmt.Errorf("加载路由模板失败: %v", err)
	}

	// 创建输出目录
	outputDir := filepath.Join(config.Generate.OutputPath, "internal/api/routers")
	if err := os.MkdirAll(outputDir, 0755); err != nil {
		return fmt.Errorf("创建输出目录失败: %v", err)
	}

	// 创建输出文件
	outputFile := filepath.Join(outputDir, strings.ToLower(structName)+"Router.go")
	file, err := os.Create(outputFile)
	if err != nil {
		return fmt.Errorf("创建输出文件失败: %v", err)
	}
	defer file.Close()

	// 执行模板
	if err := tmpl.Execute(file, data); err != nil {
		return fmt.Errorf("执行模板失败: %v", err)
	}

	fmt.Printf("路由代码已生成: %s\n", outputFile)
	return nil
}

// 注册路由到主路由文件
func registerRouter(structName string, config *Config) error {
	// 路由注册文件路径
	routerFile := filepath.Join(config.Generate.OutputPath, "internal/api/routers/enter.go")

	// 读取文件内容
	content, err := os.ReadFile(routerFile)
	if err != nil {
		return fmt.Errorf("读取路由文件失败: %v", err)
	}

	// 检查是否已经注册
	if strings.Contains(string(content), fmt.Sprintf("%sRouter(privateAPI, publicAPI)", structName)) {
		// 已经注册，不需要重复添加
		return nil
	}

	// 查找是否已经有业务模块路由注册的部分
	businessRouteRegex := regexp.MustCompile(`// 注册业务模块路由\s*([^\n]*)`)
	matches := businessRouteRegex.FindStringSubmatch(string(content))

	if len(matches) > 0 {
		// 已经有业务模块路由注册的部分，在其后添加新路由
		newContent := strings.Replace(
			string(content),
			matches[0],
			fmt.Sprintf("%s\n\t%sRouter(privateAPI, publicAPI)", matches[0], structName),
			1,
		)

		// 写入文件
		if err := os.WriteFile(routerFile, []byte(newContent), 0644); err != nil {
			return fmt.Errorf("写入路由文件失败: %v", err)
		}
	} else {
		// 没有业务模块路由注册的部分，需要添加
		// 查找SystemRouter注册的位置
		systemRouterRegex := regexp.MustCompile(`// 注册系统相关路由\s*SystemRouter\(privateAPI\)(\s*\n\s*)`)
		matches = systemRouterRegex.FindStringSubmatch(string(content))

		if len(matches) > 0 {
			// 在SystemRouter后添加业务模块路由注册
			newContent := strings.Replace(
				string(content),
				matches[0],
				fmt.Sprintf("// 注册系统相关路由\n\tSystemRouter(privateAPI)%s\n\t// 注册业务模块路由\n\t%sRouter(privateAPI, publicAPI)\n", matches[1], structName),
				1,
			)

			// 写入文件
			if err := os.WriteFile(routerFile, []byte(newContent), 0644); err != nil {
				return fmt.Errorf("写入路由文件失败: %v", err)
			}
		} else {
			// 找不到SystemRouter，尝试在registerAPIRoutes函数末尾添加
			registerAPIRoutesRegex := regexp.MustCompile(`func registerAPIRoutes\([^)]+\) {([\s\S]*?)(}\s*\n)`)
			matches = registerAPIRoutesRegex.FindStringSubmatch(string(content))

			if len(matches) > 0 {
				// 在函数末尾添加业务模块路由注册
				newContent := strings.Replace(
					string(content),
					matches[0],
					fmt.Sprintf("func registerAPIRoutes(r *gin.Engine) {%s\n\t// 注册业务模块路由\n\t%sRouter(privateAPI, publicAPI)\n%s", matches[1], structName, matches[2]),
					1,
				)

				// 写入文件
				if err := os.WriteFile(routerFile, []byte(newContent), 0644); err != nil {
					return fmt.Errorf("写入路由文件失败: %v", err)
				}
			} else {
				return fmt.Errorf("无法找到合适的位置注册路由")
			}
		}
	}

	fmt.Printf("路由注册已添加: %sRouter\n", structName)
	return nil
}

// 更新API入口文件
func updateAPIEnter(structName string, config *Config) error {
	// API入口文件路径
	apiEnterFile := filepath.Join(config.Generate.OutputPath, "internal/api/v1/enter.go")

	// 检查文件是否存在
	if _, err := os.Stat(apiEnterFile); os.IsNotExist(err) {
		// 文件不存在，创建新文件
		content := fmt.Sprintf(`package v1

import (
	"5title_gin/internal/api/v1/%s"
	"5title_gin/internal/api/v1/system"
	"5title_gin/internal/api/v1/user"
)

// ApiGroup API组
type ApiGroup struct {
	UserApi   user.UserApi
	%sApi  %s.%sAPI
}

// App API实例
var App = new(ApiGroup)
`, config.Generate.ModuleName, structName, config.Generate.ModuleName, structName)

		if err := os.WriteFile(apiEnterFile, []byte(content), 0644); err != nil {
			return fmt.Errorf("创建API入口文件失败: %v", err)
		}

		return nil
	}

	// 读取文件内容
	content, err := os.ReadFile(apiEnterFile)
	if err != nil {
		return fmt.Errorf("读取API入口文件失败: %v", err)
	}

	// 检查是否已经添加
	if strings.Contains(string(content), fmt.Sprintf("%sApi", structName)) {
		// 已经添加，不需要重复添加
		return nil
	}

	// 添加导入
	importRegex := regexp.MustCompile(`import \(([\s\S]*?)\)`)
	if !strings.Contains(string(content), fmt.Sprintf(`"%s"`, config.Generate.ModuleName)) {
		newImport := fmt.Sprintf(`import (
	"5title_gin/internal/api/v1/%s"$1)`, config.Generate.ModuleName)
		content = []byte(importRegex.ReplaceAllString(string(content), newImport))
	}

	// 添加API字段
	typeRegex := regexp.MustCompile(`type ApiGroup struct {([\s\S]*?)}`)
	newType := fmt.Sprintf(`type ApiGroup struct {$1
	%sApi  %s.%sAPI
}`, structName, config.Generate.ModuleName, structName)
	content = []byte(typeRegex.ReplaceAllString(string(content), newType))

	// 写入文件
	if err := os.WriteFile(apiEnterFile, content, 0644); err != nil {
		return fmt.Errorf("写入API入口文件失败: %v", err)
	}

	fmt.Printf("API入口文件已更新: 添加 %sApi\n", structName)
	return nil
}

// 将数据库类型转换为Go类型
func convertToGoType(dbType string) string {
	dbType = strings.ToLower(dbType)

	if strings.Contains(dbType, "int") {
		return "int"
	} else if strings.Contains(dbType, "char") || strings.Contains(dbType, "text") {
		return "string"
	} else if strings.Contains(dbType, "datetime") || strings.Contains(dbType, "timestamp") {
		return "time.Time"
	} else if strings.Contains(dbType, "decimal") || strings.Contains(dbType, "double") {
		return "float64"
	} else if strings.Contains(dbType, "float") {
		return "float32"
	} else if strings.Contains(dbType, "bool") {
		return "bool"
	}

	return "string" // 默认类型
}

// 转换为驼峰命名
func toCamelCase(s string, upperFirst bool) string {
	parts := strings.Split(s, "_")
	for i := 0; i < len(parts); i++ {
		if i == 0 && !upperFirst {
			continue
		}
		if len(parts[i]) > 0 {
			parts[i] = strings.ToUpper(parts[i][:1]) + parts[i][1:]
		}
	}
	return strings.Join(parts, "")
}
